//
// Created by Baymax on 2023/3/1.
//
#include <stdio.h>
#include "aiwendb.h"
#include <stdlib.h>
#include "cJSON.h"

#define LOCAL static
//加载awdb文件
#include <stdint.h>
#include <ctype.h>

#ifdef _WIN32
#else
#include <sys/stat.h>
#include <arpa/inet.h>
#include <string.h>
#include <json-c/json.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#endif

int is_numeric_string(const char *str) {
    while (*str) {
        if (!isdigit(*str)) {
            return 0;
        }
        str++;
    }
    return 1;
}

static size_t
awdb_strnlen(const char *s, size_t maxlen) {
    size_t len;

    for (len = 0; len < maxlen; len++, s++) {
        if (!*s)
            break;
    }
    return (len);
}


static char *
awdb_strndup(const char *str, size_t n) {
    size_t len;
    char *copy;

    len = awdb_strnlen(str, n);
    if ((copy = malloc(len + 1)) == NULL)
        return (NULL);
    memcpy(copy, str, len);
    copy[len] = '\0';
    return (copy);
}

int AWDB_resolve(FILE *const stream, AWDB_s *reader, AIWEN_result *result) {
    const uint8_t *data_section = reader->file_content;
    if (reader->aiwen_metadata.decode_type == 1) {
        resolve(stream, reader, result, result->entry.offset, 0, 0, data_section);
    } else if (reader->aiwen_metadata.decode_type == 2) {
        resolve2(stream, reader, result);
    }
    return ErrNoErr;
}

unsigned int uint_from_bytes(const char *b, int len) {
    unsigned int value = 0;
    for (int i = 0; i < len; i++) {
        value = (value << 8) | (unsigned char) b[i];
    }
    return value;
}

unsigned int l2b(unsigned int x) {
    return (((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00) | ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000));

}


AIWEN_result AWDB_reader_find(AWDB_s *reader, const char *addr) {
    AIWEN_result result = {
            .is_found = false,
            .netmask     = 0,
            .entry       = {
                    .awdb    = reader,
                    .offset  = 0
            }
    };
    int is_find = AWDB_find0(reader, &result, addr);
    if (is_find == 0) {
        result.is_found = true;
        result.index_offset = 0;
    }
    return result;
}


int AWDB_find0(AWDB_s *reader, AIWEN_result *result, const char *addr) {
    unsigned int node = 0;
    int err;
    if ( is_numeric_string(addr) == 1) {
        uint64_t ip_int = strtoull(addr, NULL, 10);
        if (ip_int < (1ull << 32)) {
            if (strcmp(reader->aiwen_metadata.ip_version, "4") == 0) {
//                printf("ip_int is %lld\n", ip_int);
                uint32_t net_num = htonl(ip_int);
                err = AWDB_search(reader, (const u_char *) &net_num, 32, &node);
            } else if(strcmp(reader->aiwen_metadata.ip_version, "4_6") ==0) {
                struct in6_addr addr6;
                //char *ip_4_6 =(char *) malloc(INET6_ADDRSTRLEN + 1);
                char ip_4_6[INET6_ADDRSTRLEN + 1];
                int addr_1 = ip_int >> 24;        // 提取第一部分IP地址
                ip_int = ip_int << 40;
                int addr_2 = ip_int >> 56;        // 提取第二部分IP地址
                ip_int  = ip_int << 8;
                int addr_3 = ip_int >> 56;        // 提取第三部分IP地址
                ip_int  = ip_int  << 8;
                int addr_4 = ip_int  >> 56;
                sprintf(ip_4_6,"::ffff:%d.%d.%d.%d",addr_1,addr_2,addr_3,addr_4);
                inet_pton(AF_INET6, ip_4_6, &addr6);
                err = AWDB_search(reader, (const u_char *) &addr6.s6_addr, 128, &node);
                //free(ip_4_6);
            }else {
                printf("Your int ip version is ipv4 but the lib is ipv6\n");
                exit(2);
            }
        } else {
            printf("Your int ip version is not ipv4\n");
            exit(3);
        }
    } else {
        struct in_addr addr4;
        struct in6_addr addr6;
        if (inet_pton(AF_INET, addr, &addr4)) {   // ip 转成二进制字节
            if (strcmp(reader->aiwen_metadata.ip_version, "4") == 0) {
                err = AWDB_search(reader, (const u_char *) &addr4.s_addr, 32, &node);
                if (err != ErrNoErr) {
                    return err;
                }
            }else if(strcmp(reader->aiwen_metadata.ip_version, "4_6") == 0) {
                //char *new_ip =(char *) malloc(INET6_ADDRSTRLEN + 1);
                char new_ip[INET6_ADDRSTRLEN + 1];
                sprintf(new_ip,"::ffff:%s",addr);
                inet_pton(AF_INET6, new_ip, &addr6);
                err = AWDB_search(reader, (const u_char *) &addr6.s6_addr, 128, &node);
                //free(new_ip);
            }
            else {
                printf("Your ip version is ipv4 but the lib is ipv6 \n");
                exit(3);
            }
        } else if (inet_pton(AF_INET6, addr, &addr6)) {
            // printf("查找 ipv6 节点数据\n");
            if (strcmp(reader->aiwen_metadata.ip_version, "4") != 0) {
                err = AWDB_search(reader, (const u_char *) &addr6.s6_addr, 128, &node);
                if (err != ErrNoErr) {
                    return err;
                }
            } else {
                printf("Your ip version is ipv6 but the lib is ipv%s\n", reader->aiwen_metadata.ip_version);
                exit(3);
            }
        } else {
            printf("Invalid ip %s\n", addr);
            exit(3);
        }
    }
    result->entry.bash_offset = reader->aiwen_metadata.node_count * reader->aiwen_metadata.byte_len * 2 + 2 +
                                reader->aiwen_metadata.metadata_len;
    result->entry.offset = result->entry.bash_offset + node - reader->aiwen_metadata.node_count - 10;
    return err;
}

int AWDB_search(AWDB_s *reader, const u_char *ip, int bit_count, unsigned int *node) {
    *node = 0;
    const uint8_t *search_tree = reader->file_content;
    for (int i = 0; i < bit_count; ++i) {
        if (*node > reader->aiwen_metadata.node_count) {
            break;
        }
        *node = AWDB_read_node(reader, *node, ((0xFF & ((int) ip[i >> 3])) >> (unsigned int) (7 - (i % 8))) & 1,
                               search_tree);
    }
    if (*node > reader->aiwen_metadata.node_count) {
//        printf("node is %d\n", *node);
        return ErrNoErr;
    }
    return ErrDataNotExists;
}

int print_(FILE *stream, int num, int list_len) {
    if (num >= 0 && num < list_len - 1) {
        fprintf(stream, ",\n");
    }
    return 0;
}

int decode_list(FILE *stream, AWDB_s *reader, AIWEN_result *result, int offset, int deep,
                const uint8_t *data_section) {
    if (deep != 2) {
        fprintf(stream, "{");
    }
    int list_len = data_section[offset];
    offset += 1;
//    printf("list len is %d\n", list_len);
    for (int i = 0; i < list_len; i++) {   // 循环列表长度
        if (deep == 1) {
            fprintf(stream, "\"%s\":", reader->aiwen_metadata.columns[i + result->index_offset]);
            offset = resolve(stream, reader, result, offset, deep, i, data_section);
        } else if (deep == 2) {
            offset = resolve(stream, reader, result, offset, deep, i, data_section);
        } else if (deep == 3) {
//            sprintf(r_str, "%s'%s':", r_str, reader->list.list[i]);
            fprintf(stream, "\"%s\":", reader->list.list[i]);
            offset = resolve(stream, reader, result, offset, deep, i, data_section);
        }
        print_(stream, i, list_len);
    }
    if (deep != 2) {
        fprintf(stream, "}\n");
    }
    return offset;
}


uint32_t decode_pointer(FILE *stream, AWDB_s *reader, AIWEN_result *result, uint32_t offset, int index,
                        const uint8_t *data_section) {
    int pointer_len = data_section[offset];
//    printf("pointer_len is %d \n", pointer_len);
    offset += 1;
    uint32_t buf = uint_from_bytes(data_section + offset, pointer_len); // 获取指针位置
    offset += pointer_len;  // 加上指针长度
    uint32_t p_offset = result->entry.bash_offset + buf; // 指针 offset
    resolve(stream, reader, result, p_offset, 0, index, data_section);
    return offset;
}


uint32_t decode_str(FILE *stream, int offset, const uint8_t *data_section) {

    int str_len = data_section[offset];
    offset += 1;
//    char *data = (char *) malloc(str_len + 1);
//    memcpy(data, data_section + offset, str_len);
//    data[str_len] = '\0';
    char *data = awdb_strndup((char *) &data_section[offset], str_len);
    fprintf(stream, "\"%s\"", data);
    offset += str_len;
    free(data);
    return offset;
}


uint32_t decode_long_str(FILE *stream, int offset, const uint8_t *data_section) {
    int long_str_len = data_section[offset];
    offset += 1;
    uint32_t buf = 0;
    buf = uint_from_bytes(data_section + offset, long_str_len); // 获取指针位置
    offset += long_str_len;
    char *data = awdb_strndup((char *) &data_section[offset], buf);
    fprintf(stream, "\"%s\"", data);
    free(data);
    int new_offset = offset + buf;
    return new_offset;
}

uint32_t decode_uint(FILE *stream, int offset, const uint8_t *data_section) {
    int uint_len = data_section[offset];
    offset += 1;
    uint32_t buf = uint_from_bytes(data_section + offset, uint_len);
    offset += uint_len;
    fprintf(stream, "%u", buf);
    return offset;
}

uint32_t decode_int(FILE *stream, int offset, const uint8_t *data_section) {
    int32_t r;
    memcpy(&r, data_section + offset, 4);
    r = (int32_t) ntohl(r);
    offset += 4;

    fprintf(stream, "%d", r);
    return offset;
}

uint32_t decode_float(FILE *stream, int offset, const uint8_t *data_section) {
    volatile float f;
    uint8_t *q = (void *) &f;
/* Windows builds don't use autoconf but we can assume they're all
 * little-endian. */
// #if  _WIN32
    q[3] = data_section[offset];
    q[2] = data_section[offset + 1];
    q[1] = data_section[offset + 2];
    q[0] = data_section[offset + 3];
// #else
//     memcpy(q, data_section + offset, sizeof(float));
// #endif
    fprintf(stream, "%f", f);
    return offset + 4;
}


uint32_t decode_double(FILE *stream, int offset, const uint8_t *data_section) {
    volatile double d;
    uint8_t *q = (void *) &d;
// #if _WIN32
    q[7] = data_section[offset];
    q[6] = data_section[offset + 1];
    q[5] = data_section[offset + 2];
    q[4] = data_section[offset + 3];
    q[3] = data_section[offset + 4];
    q[2] = data_section[offset + 5];
    q[1] = data_section[offset + 6];
    q[0] = data_section[offset + 7];
// #else
//     memcpy(q, data_section + offset , 8);
// #endif
    fprintf(stream, "%lf", d);
    return offset + 8;
}

char *strreplace(const char *str, const char *old, const char *new) { // 替换字符串内容
    char *result;
    int i, cnt = 0;
    int newlen = strlen(new);
    int oldlen = strlen(old);

    // count the number of times old occurs in str
    for (i = 0; str[i] != '\0'; i++) {
        if (strstr(&str[i], old) == &str[i]) {
            cnt++;
            i += oldlen - 1;
        }
    }

    // allocate memory for the new string
    result = (char *) malloc(i + cnt * (newlen - oldlen) + 1);

    i = 0;
    while (*str) {
        // compare the substring with the result
        if (strstr(str, old) == str) {
            strcpy(&result[i], new);
            i += newlen;
            str += oldlen;
        } else {
            result[i++] = *str++;
        }
    }
    result[i] = '\0';
    return result;
}

char *trim(char *str) { // 去除前后端空格
    int len = strlen(str);
    char *start = str;
    char *end = str + len - 1;

    // trim leading spaces
    while (isspace(*start)) {
        start++;
    }
    // trim trailing spaces
    while (isspace(*end) && end >= start) {
        end--;
    }
    // add null terminator to the end of the trimmed string
    *(end + 1) = '\0';

    return start;
}


//字符转 list
StringList str_2_list(const char *str) {
    StringList result;
    result.size = 0;
    char *r = strreplace(str, "\"", "");
    char *tmp = r; // save strreplace
    r = strreplace(r, ",", "");
    free(tmp);
    tmp = r;
    r = strreplace(r, "[", "");
    free(tmp); //
    tmp = r;
    r = strreplace(r, "]", "");
    free(tmp);
    r = trim(r);
    char *token = strtok(r, " ");
    while (token != NULL) {
        result.list[result.size++] = token;
        token = strtok(NULL, " ");
    }
    return result;
}

uint32_t resolve(FILE *stream, AWDB_s *reader, AIWEN_result *result, int offset, int deep, int index,
                 const uint8_t *data_section) {
    int data_type = data_section[offset];
    offset += 1;
//     printf("this type is :%d\n", data_type);
    if (data_type == 1) { // 列表
        deep += 1;
        if (deep == 2) {
            fprintf(stream, "[");
            result->index_offset += 1;
            if (reader->ls_s == 0) {
                reader->list = str_2_list(reader->aiwen_metadata.columns[index + 1]);
                reader->ls_s += 1;
            }
        }
        offset = decode_list(stream, reader, result, offset, deep, data_section);
        if (deep == 2) {
            fprintf(stream, "]");
        }
    } else if (data_type == 2) {  // 指针
        offset = decode_pointer(stream, reader, result, offset, index, data_section);
    } else if (data_type == 3) {  // 字符串
        offset = decode_str(stream, offset, data_section);
    } else if (data_type == 4) {  // long str
        offset = decode_long_str(stream, offset, data_section);
    } else if (data_type == 5) { // uint
        offset = decode_uint(stream, offset, data_section);
    } else if (data_type == 6) { // int
        offset = decode_int(stream, offset, data_section);
    } else if (data_type == 7) { //float
        offset = decode_float(stream, offset, data_section);
    } else if (data_type == 8) { // double
        offset = decode_double(stream, offset, data_section);
    }
    return offset;
}

char *trimt(char *str) {
    char *end;
    while (*str == '\t') str++;
    if (*str == 0)
        return str;
    end = str + strlen(str) - 1;
    while (end > str && *end == '\t') end--;
    *(end+1) = '\0';
    return str;
}


uint32_t resolve2(FILE *stream, AWDB_s *reader, AIWEN_result *result) {
    char tmp[64];
    uint32_t buf;
    buf = uint_from_bytes(reader->file_content + result->entry.offset, 4);
    // printf("字节长度为：%d\n",buf);
    char *data = (char *) malloc(buf + 1);
    uint32_t offset = result->entry.offset + 4;
    memcpy(data, reader->file_content + offset, buf);
    fprintf(stream, "{\n");
    int f = 0, p1 = 0, p2 = -1;
    do {
        if (*(data + p1) == '\t' || !*(data + p1)) {
            strncpy(tmp, data + p2 + 1, (size_t) p1 - p2);
            tmp[p1 - p2] = 0;
            fprintf(stream, "\"%s\":\"%s\"\n", reader->aiwen_metadata.columns[f], trimt(tmp));
            p2 = p1;
            ++f;
        }
    } while (*(data + p1++));
    fprintf(stream, "}\n");
    free(data);
    return 0;
}


unsigned int AWDB_read_node(AWDB_s *reader, int node, int index, const uint8_t *search_tree) {
    int off = node * reader->aiwen_metadata.byte_len * 2 + index * reader->aiwen_metadata.byte_len + 2 +
              reader->aiwen_metadata.metadata_len;
    if (reader->aiwen_metadata.byte_len == 4) {
        int tar = *(int *) &search_tree[off];
        unsigned int r = l2b((unsigned int) tar);
        return r;
    } else {
        uint32_t buf = uint_from_bytes(search_tree + off, reader->aiwen_metadata.byte_len);
        return buf;
    }
}


LOCAL int get_aiwen_metadata(AWDB_s *awdb, int meta_lneth) {
    char *meta_json = awdb_strndup((char *) &awdb->file_content[2], meta_lneth);
//    printf("%s\n", meta_json);
    cJSON *root = cJSON_Parse(meta_json);
    cJSON *decode_type = cJSON_GetObjectItemCaseSensitive(root, "decode_type");
    cJSON *node_count = cJSON_GetObjectItemCaseSensitive(root, "node_count");
    cJSON *ip_version = cJSON_GetObjectItemCaseSensitive(root, "ip_version");
    cJSON *byte_len = cJSON_GetObjectItemCaseSensitive(root, "byte_len");
    cJSON *columns = cJSON_GetObjectItemCaseSensitive(root, "columns");

    // awdb->aiwen_metadata.decode_type = 1;
    awdb->aiwen_metadata.decode_type = decode_type->valueint;
    awdb->aiwen_metadata.node_count = node_count->valueint;
    awdb->aiwen_metadata.ip_version = ip_version->valuestring;
    awdb->aiwen_metadata.byte_len = byte_len->valueint;
    awdb->aiwen_metadata.columns_length = cJSON_GetArraySize(columns);
    awdb->aiwen_metadata.columns = (char **) malloc(sizeof(char *) * awdb->aiwen_metadata.columns_length);

    for (int i = 0; i < awdb->aiwen_metadata.columns_length; ++i) {
        cJSON *item = cJSON_GetArrayItem(columns, i);
        if (item->valuestring == NULL) {
            char *str = cJSON_Print(item);
            awdb->aiwen_metadata.columns[i] = malloc(sizeof(char) * strlen(str) + 1);
            strcpy(awdb->aiwen_metadata.columns[i], str);
        } else {
            awdb->aiwen_metadata.columns[i] = malloc(sizeof(char) * strlen(item->valuestring) + 1);
            strcpy(awdb->aiwen_metadata.columns[i], item->valuestring);
        }
//        json_object *it = json_object_array_get_idx(value, i);
    }
    if (strcmp(awdb->aiwen_metadata.ip_version, "4") == 0) {
//        printf("awdb->aiwen_metadata.node_count  %s\n", awdb->aiwen_metadata.ip_version);
        awdb->depth = 32;
    } else {
        awdb->depth = 128;
    }
    awdb->ls_s = 0;
    free(meta_json);
    return 1;
}


#ifdef _WIN32
LOCAL int aiwen_load(AWDB_s *const awdb) {
    DWORD size;
    int status = 0;
    HANDLE mmh = NULL;
    HANDLE fd = INVALID_HANDLE_VALUE;
    fd = CreateFile(awdb->filename, GENERIC_READ, FILE_SHARE_READ, NULL,
                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (fd == INVALID_HANDLE_VALUE) {
        printf("file is not exists;\n");
        exit(3);
    }
    size = GetFileSize(fd, NULL);
    mmh = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, size, NULL);
    uint8_t *file_content =
            (uint8_t *) MapViewOfFile(mmh, FILE_MAP_READ, 0, 0, 0);
    awdb->file_size = size;
    awdb->file_content = file_content;
    int saved_errno = errno;
    if (INVALID_HANDLE_VALUE != fd) {
        CloseHandle(fd);
    }
    if (NULL != mmh) {
        CloseHandle(mmh);
    }
    errno = saved_errno;

    return status;
}

#else
LOCAL int aiwen_load(AWDB_s *const awdb) {
    ssize_t size;
    int status = 0;
    int flags = O_RDONLY;
    flags |= O_CLOEXEC;
    int fd = open(awdb->filename, flags);
    struct stat s;
    if (fd < 0 || fstat(fd, &s)) {
        printf("file is not exists;\n");
        exit(3);
    }
    size = s.st_size;
    uint8_t *file_content =
            (uint8_t *) mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
    awdb->file_size = size;
    awdb->file_content = file_content;
    close(fd);
    return status;
}
#endif

int AWDB_file_open(const char *const filename, AWDB_s *const awdb) {
    awdb->filename = filename;
    aiwen_load(awdb);
    uint16_t metadata_lenth = (awdb->file_content[0] << 8) | awdb->file_content[1];
    awdb->aiwen_metadata.metadata_len = metadata_lenth;
    // printf("metadata_lenth is %d\n", metadata_lenth);

    get_aiwen_metadata(awdb, metadata_lenth);
    return 1;
}

void AWDB_close(AWDB_s *const awdb) {
    if (!awdb) {
        return;
    }
    if (NULL != awdb->file_content) {

#ifdef _WIN32
        UnmapViewOfFile(awdb->file_content);
        WSACleanup();
#else
        munmap((void *)awdb->file_content, awdb->file_size);
#endif
    }
}

